package com.bolt.common.utils;


import com.bolt.common.lang.Assert;
import com.bolt.convention.exception.UtilException;

import java.text.ParseException;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;

public class DateUtil {

    public final static String DEFAULT_TIMEZONE = "GMT+8";

    public final static String ISO_DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS";

    public final static String ISO_SHORT_DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss";

    public final static String DEFAULT_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";

    public final static String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";

    public final static String SHORT_TIME_FORMAT = "yyyy-MM-dd HH:mm";

    public final static String FULL_SEQ_FORMAT = "yyyyMMddHHmmssSSS";

    public final static String[] MULTI_FORMAT = {DEFAULT_DATE_FORMAT,
            DEFAULT_TIME_FORMAT, ISO_DATE_TIME_FORMAT,
            ISO_SHORT_DATE_TIME_FORMAT, SHORT_TIME_FORMAT, "yyyy-MM"};

    public final static String FORMAT_YYYY = "yyyy";

    public final static String FORMAT_YYYYMM = "yyyyMM";

    public final static String FORMAT_YYYYMMDD = "yyyyMMdd";

    public final static String FORMAT_YYYYMMDDHH = "yyyyMMddHH";

    /**
     * 日期格式化(默认yyyy-MM-dd)
     * @param date
     * @return
     */
    public static String formatDate(Date date) {
        if (date == null) {
            return "";
        }
        return new SimpleDateFormat(DEFAULT_DATE_FORMAT).format(date);
    }

    /**
     *  日期格式化(
     * @param date 日期
     * @param format 格式化参数
     * @return
     */
    public static String formatDate(Date date, String format) {
        if (date == null) {
            return null;
        }
        if (StrUtil.isBlank(format)) {
            format = DEFAULT_DATE_FORMAT;
        }
        return new SimpleDateFormat(format).format(date);
    }

    /**
     * 格式化日期转Integer类型
     * @param date
     * @param format
     * @return
     */
    public static Integer formatDateToInt(Date date, String format) {
        if (date == null) {
            return null;
        }
        return Integer.valueOf(new SimpleDateFormat(format).format(date));
    }

    /**
     * 格式化日期转Long类型
     * @param date
     * @param format
     * @return
     */
    public static Long formatDateToLong(Date date, String format) {
        if (date == null) {
            return null;
        }
        return Long.valueOf(new SimpleDateFormat(format).format(date));
    }

    /**
     * 日期格式化(yyyy-MM-dd HH:mm:ss)
     * @param date
     * @return
     */
    public static String formatTime(Date date) {
        if (date == null) {
            return null;
        }
        return new SimpleDateFormat(DEFAULT_TIME_FORMAT).format(date);
    }
    /**
     * 日期格式化(yyyy-MM-dd HH:mm)
     * @param date
     * @return
     */
    public static String formatShortTime(Date date) {
        if (date == null) {
            return null;
        }
        return new SimpleDateFormat(SHORT_TIME_FORMAT).format(date);
    }
    /**
     * 当前日期格式化(yyyy-MM-dd）
     */
    public static String formatDateNow() {
        return formatDate(DateUtil.currentDate());
    }
    /**
     * 当前日期格式化(yyyy-MM-dd HH:mm:ss)
     * @return
     */
    public static String formatTimeNow() {
        return formatTime(DateUtil.currentDate());
    }


    /**
     * 字符串转日期类型
     *
     * @param date
     * @param format
     * @return
     */
    public static Date parseDate(String date, String format) {
        return parseDate(date, null, format);
    }

    /**
     * 字符串转日期类型
     *
     * @param date
     * @return
     */
    public static Date parseDate(String date) {
        try {
            return parseDate(date, null, DEFAULT_TIME_FORMAT);
        } catch (Exception ex) {
            return parseDate(date, null, DEFAULT_DATE_FORMAT);
        }
    }

    /**
     * 字符串转日期类型
     *
     * @param str
     * @param locale
     * @param parsePatterns
     * @return
     */
    public static Date parseDate(final String str, final Locale locale, final String... parsePatterns) {
        if (str == null || parsePatterns == null) {
            throw new IllegalArgumentException("Date and Patterns must not be null");
        }
        SimpleDateFormat parser;
        if (locale == null) {
            parser = new SimpleDateFormat();
        } else {
            parser = new SimpleDateFormat("", locale);
        }

        parser.setLenient(false);
        final ParsePosition pos = new ParsePosition(0);
        for (final String parsePattern : parsePatterns) {

            String pattern = parsePattern;

            // LANG-530 - need to make sure 'ZZ' output doesn't get passed to SimpleDateFormat
            if (parsePattern.endsWith("ZZ")) {
                pattern = pattern.substring(0, pattern.length() - 1);
            }

            parser.applyPattern(pattern);
            pos.setIndex(0);

            String str2 = str;
            // LANG-530 - need to make sure 'ZZ' output doesn't hit SimpleDateFormat as it will ParseException
            if (parsePattern.endsWith("ZZ")) {
                str2 = str.replaceAll("([-+][0-9][0-9]):([0-9][0-9])$", "$1$2");
            }

            final Date date = parser.parse(str2, pos);
            if (date != null && pos.getIndex() == str2.length()) {
                return date;
            }
        }
        throw new UtilException("Unable to parse the date: " + str, -1);
    }

    /**
     * 判断是否同一天
     *
     * @param date1
     * @param date2
     * @return
     */
    public static boolean isSameDay(final Date date1, final Date date2) {
        if (date1 == null || date2 == null) {
            throw new IllegalArgumentException("The date must not be null");
        }
        final Calendar cal1 = Calendar.getInstance();
        cal1.setTime(date1);
        final Calendar cal2 = Calendar.getInstance();
        cal2.setTime(date2);
        return isSameDay(cal1, cal2);
    }


    /**
     * 判断是否同一天
     *
     * @param cal1
     * @param cal2
     * @return
     */
    public static boolean isSameDay(final Calendar cal1, final Calendar cal2) {
        if (cal1 == null || cal2 == null) {
            throw new IllegalArgumentException("The date must not be null");
        }
        return (cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) &&
                cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) &&
                cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR));
    }

    /**
     * 转换为Calendar对象
     *
     * @param date 日期对象
     * @return Calendar对象
     */
    public static Calendar calendar(Date date) {
        final Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        return cal;
    }

    /**
     * 转换为Calendar对象
     *
     * @param millis 时间戳
     * @return Calendar对象
     */
    public static Calendar calendar(long millis) {
        final Calendar cal = Calendar.getInstance();
        cal.setTimeInMillis(millis);
        return cal;
    }


    /**
     * 把类似2014-01-01 ~ 2014-01-30格式的单一字符串转换为两个元素数组
     */
    public static Date[] parseBetweenDates(String date) {
        if (StrUtil.isBlank(date)) {
            return null;
        }
        date = date.replace("～", "~");
        Date[] dates = new Date[2];
        String[] values = date.split("~");
        dates[0] = parseDate(values[0], null, MULTI_FORMAT);
        dates[1] = parseDate(values[1].trim(), null, MULTI_FORMAT);
        return dates;
    }


    /**
     * 获取指定日期偏移指定时间后的时间，生成的偏移日期不影响原日期
     *
     * @param date   基准日期  偏移的粒度大小
     * @param offset 偏移量，正数为向后偏移，负数为向前偏移
     * @param field  单位
     * @return
     */
    public static Date offset(Date date, int offset, int field) {
        final Calendar cal = calendar(date);
        cal.add(field, offset);
        return cal.getTime();
    }

    /**
     * 偏移毫秒数
     *
     * @param date   日期
     * @param offset 偏移毫秒数，正数向未来偏移，负数向历史偏移
     * @return 偏移后的日期
     */
    public static Date offsetMillisecond(Date date, int offset) {
        return offset(date, offset, Calendar.MILLISECOND);
    }

    /**
     * 偏移秒数
     *
     * @param date   日期
     * @param offset 偏移秒数，正数向未来偏移，负数向历史偏移
     * @return 偏移后的日期
     */
    public static Date offsetSecond(Date date, int offset) {
        return offset(date, offset, Calendar.SECOND);
    }

    /**
     * 偏移分钟
     *
     * @param date   日期
     * @param offset 偏移分钟数，正数向未来偏移，负数向历史偏移
     * @return 偏移后的日期
     */
    public static Date offsetMinute(Date date, int offset) {
        return offset(date, offset, Calendar.MINUTE);
    }

    /**
     * 偏移小时
     *
     * @param date   日期
     * @param offset 偏移小时数，正数向未来偏移，负数向历史偏移
     * @return 偏移后的日期
     */
    public static Date offsetHour(Date date, int offset) {
        return offset(date, offset, Calendar.HOUR_OF_DAY);
    }

    /**
     * 偏移天
     *
     * @param date   日期
     * @param offset 偏移天数，正数向未来偏移，负数向历史偏移
     * @return 偏移后的日期
     */
    public static Date offsetDay(Date date, int offset) {
        return offset(date, offset, Calendar.DAY_OF_YEAR);
    }

    /**
     * 偏移周
     *
     * @param date   日期
     * @param offset 偏移周数，正数向未来偏移，负数向历史偏移
     * @return 偏移后的日期
     */
    public static Date offsetWeek(Date date, int offset) {
        return offset(date, offset, Calendar.WEEK_OF_YEAR);
    }

    /**
     * 偏移月
     *
     * @param date   日期
     * @param offset 偏移月数，正数向未来偏移，负数向历史偏移
     * @return 偏移后的日期
     */
    public static Date offsetMonth(Date date, int offset) {
        return offset(date, offset, Calendar.MONTH);
    }

    /**
     * @Title:getDiffDay
     * @Description:获取日期相差天数
     * @param:@param beginDate 字符串类型开始日期
     * @param:@param endDate 字符串类型结束日期
     * @param:@return
     * @return:Long 日期相差天数
     * @author:谢
     * @thorws:
     */
    public static Long getDiffDay(String beginDate, String endDate) {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
        Long checkday = 0l;
        // 开始结束相差天数
        try {
            checkday = (formatter.parse(endDate).getTime() - formatter.parse(
                    beginDate).getTime())
                    / (1000 * 24 * 60 * 60);
        } catch (ParseException e) {

            e.printStackTrace();
            checkday = null;
        }
        return checkday;
    }

    /**
     * @Title:getDiffDay
     * @Description:获取日期相差天数
     * @param:@param beginDate Date类型开始日期
     * @param:@param endDate Date类型结束日期
     * @param:@return
     * @return:Long 相差天数
     * @thorws:
     */
    public static Long getDiffDay(Date beginDate, Date endDate) {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String strBeginDate = format.format(beginDate);

        String strEndDate = format.format(endDate);
        return getDiffDay(strBeginDate, strEndDate);
    }

    /**
     * N天之后
     *
     * @param n
     * @param date
     * @return
     */
    public static Date nDaysAfter(Integer n, Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.set(Calendar.DAY_OF_MONTH, cal.get(Calendar.DAY_OF_MONTH) + n);
        return cal.getTime();
    }

    /**
     * N月之后
     *
     * @param n
     * @param date
     * @return
     */
    public static Date nMonthAfter(Integer n, Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.set(Calendar.MONDAY, cal.get(Calendar.MONDAY) + n);
        return cal.getTime();
    }

    /**
     * N天之前
     *
     * @param n
     * @param date
     * @return
     */
    public static Date nDaysAgo(Integer n, Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.set(Calendar.DAY_OF_MONTH, cal.get(Calendar.DAY_OF_MONTH) - n);
        return cal.getTime();
    }

    /**
     * N月之前
     *
     * @param n
     * @param date
     * @return
     */
    public static Date nMonthsAgo(Integer n, Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.set(Calendar.MONDAY, cal.get(Calendar.MONDAY) - n);
        return cal.getTime();
    }

    public static Date currentDate() {
        return new Date();
    }

    public static Calendar toCalendar(final Date date) {
        final Calendar c = Calendar.getInstance();
        c.setTime(date);
        return c;
    }

    /**
     * 取某月的第一天
     * @param date
     * @return
     */
    public static Date getMonthFristDate(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.set(Calendar.DAY_OF_MONTH, 1);
        return cal.getTime();
    }

    /**
     * 取某月的最后一天
     * @param date
     * @return
     */
    public static Date getMonthLastDate(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        //获取某月最大天数
        int lastDay = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
        //设置日历中月份的最大天数
        cal.set(Calendar.DAY_OF_MONTH, lastDay);
        return cal.getTime();
    }

    /**
     * 取日期的星期
     * @param date
     * @return
     */
    public static Integer getWeekOfYear(Date date) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        return Integer.valueOf(formatDate(date, FORMAT_YYYY)
                + c.get(Calendar.WEEK_OF_YEAR));
    }

    public static long Date2Long(int year, int month, int date) {
        Calendar cld = Calendar.getInstance();
        month = month - 1;
        cld.set(year, month, date);
        return cld.getTime().getTime();
    }

    public static long Time2Long(int year, int month, int date, int hour,
                                 int minute, int second) {
        Calendar cld = Calendar.getInstance();
        month = month - 1;
        cld.set(year, month, date, hour, minute, second);
        return cld.getTime().getTime();
    }

    public static int getYear(long t) {
        Calendar cld = Calendar.getInstance();
        if (t > 0) {
            cld.setTime(new Date(t));
        }
        return cld.get(Calendar.YEAR);
    }

    public static int getMonth(long t) {
        Calendar cld = Calendar.getInstance();
        if (t > 0) {
            cld.setTime(new Date(t));
        }
        return cld.get(Calendar.MONTH) + 1;
    }

    public static int getDay(long t) {
        Calendar cld = Calendar.getInstance();
        if (t > 0) {
            cld.setTime(new Date(t));
        }
        return cld.get(Calendar.DAY_OF_MONTH);
    }

    public static int getHour(long t) {
        Calendar cld = Calendar.getInstance();
        if (t > 0) {
            cld.setTime(new Date(t));
        }
        return cld.get(Calendar.HOUR_OF_DAY);
    }

    public static int getMinute(long t) {
        Calendar cld = Calendar.getInstance();
        if (t > 0) {
            cld.setTime(new Date(t));
        }
        return cld.get(Calendar.MINUTE);
    }

    public static int getSecond(long t) {
        Calendar cld = Calendar.getInstance();
        if (t > 0) {
            cld.setTime(new Date(t));
        }
        return cld.get(Calendar.SECOND);
    }

    public static int getYear(Date date) {
        Calendar cld = Calendar.getInstance();
        cld.setTime(date);
        return cld.get(Calendar.YEAR);
    }

    public static int getMonth(Date date) {
        Calendar cld = Calendar.getInstance();
        cld.setTime(date);
        return cld.get(Calendar.MONTH) + 1;
    }

    public static int getDay(Date date) {
        Calendar cld = Calendar.getInstance();
        cld.setTime(date);
        return cld.get(Calendar.DAY_OF_MONTH);
    }

    public static int getHour(Date date) {
        Calendar cld = Calendar.getInstance();
        cld.setTime(date);
        return cld.get(Calendar.HOUR_OF_DAY);
    }

    public static int getMinute(Date date) {
        Calendar cld = Calendar.getInstance();
        cld.setTime(date);
        return cld.get(Calendar.MINUTE);
    }

    public static int getSecond(Date date) {
        Calendar cld = Calendar.getInstance();
        cld.setTime(date);
        return cld.get(Calendar.SECOND);
    }

    public static int getYear() {
        Calendar cld = Calendar.getInstance();
        cld.setTime(new Date());
        return cld.get(Calendar.YEAR);
    }

    public static int getMonth() {
        Calendar cld = Calendar.getInstance();
        cld.setTime(new Date());
        return cld.get(Calendar.MONTH) + 1;
    }

    public static int getDay() {
        Calendar cld = Calendar.getInstance();
        cld.setTime(new Date());
        return cld.get(Calendar.DAY_OF_MONTH);
    }

    public static String getDayOfWeek() {
        Calendar cld = Calendar.getInstance();
        cld.setTime(new Date());
        int w = cld.get(Calendar.DAY_OF_WEEK);
        return getDayOfWeek(w);
    }

    public static String getDayOfWeek(int w) {
        if (2 == w) {
            return "星期一";
        } else if (3 == w) {
            return "星期二";
        } else if (4 == w) {
            return "星期三";
        } else if (5 == w) {
            return "星期四";
        } else if (6 == w) {
            return "星期五";
        } else if (7 == w) {
            return "星期六";
        } else {
            return "星期日";
        }
    }

    /**
     * 是否闰年
     *
     * @param year 年
     * @return 是否闰年
     */
    public static boolean isLeapYear(int year) {
        return new GregorianCalendar().isLeapYear(year);
    }

    /**
     * 转换时间时区
     *
     * @param convertString  需要转的时间字符串
     * @param format         格式话字符串 例如d-MMM-yyyy HH:mm (z)
     * @param sourceTimeZone 源时间时区
     * @param targetTimeZone 目标时间时区
     * @return
     * @throws ParseException
     */
    public static Date converDateTimeZone(String convertString, String format, String sourceTimeZone, String targetTimeZone) {
        Date date = null;
        if (StrUtil.isEmpty(sourceTimeZone)) {
            sourceTimeZone = DEFAULT_TIMEZONE;
        }

        if (StrUtil.isEmpty(targetTimeZone)) {
            targetTimeZone = DEFAULT_TIMEZONE;
        }

        if (StrUtil.isEmpty(format)) {
            format = DEFAULT_DATE_FORMAT;
        }
        try {
            SimpleDateFormat sdf = new SimpleDateFormat(format);
            //获取传入的时间值
            Long time = new Date(sdf.parse(convertString).getTime()).getTime();
            //获取源时区时间相对的GMT时间
            Long sourceRelativelyGMT = time - TimeZone.getTimeZone(sourceTimeZone).getRawOffset();
            //GMT时间+目标时间时区的偏移量获取目标时间
            Long targetTime = sourceRelativelyGMT + TimeZone.getTimeZone(targetTimeZone).getRawOffset();
            date = new Date(targetTime);
            return date;
        } catch (Exception ex) {
            throw new UtilException("Unable to parse the date: " + convertString, -1);
        }
    }

    public static Date converDateTimeZone(Date time, String sourceTimeZone, String targetTimeZone) {
        //获取源时区时间相对的GMT时间
        Long sourceRelativelyGMT = time.getTime() - TimeZone.getTimeZone(sourceTimeZone).getRawOffset();
        //GMT时间+目标时间时区的偏移量获取目标时间
        Long targetTime = sourceRelativelyGMT + TimeZone.getTimeZone(targetTimeZone).getRawOffset();
        Date date = new Date(targetTime);
        return date;
    }

    /**
     * 蒋Date类型转换为LocalDateTime用于兼容处理一些低版本API组件
     *
     * @param date
     * @return
     */
    public static LocalDateTime convertDateToLocalDateTime(Date date, String targetTimeZone) {
        ZoneId zone;
        if (date == null) {
            return null;
        }
        if (StrUtil.isEmpty(targetTimeZone)) {
            zone = ZoneId.of(DEFAULT_TIMEZONE);
        } else {
            zone = ZoneId.of(targetTimeZone);
        }
        Instant instant = date.toInstant();
        return LocalDateTime.ofInstant(instant, zone);
    }

    /**
     * Date 转 LocalDate
     *
     * @param date
     * @return
     */
    public static LocalDate asLocalDate(Date date) {
        return Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDate();
    }

    /**
     * Date 转 LocalDateTime
     *
     * @param date
     * @return
     */
    public static LocalDateTime asLocalDateTime(Date date) {
        return Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDateTime();
    }


    /**
     * 格式化日期间隔输出
     *
     * @param beginDate 起始日期
     * @param endDate   结束日期
     * @param unit      级别，按照天、小时、分、秒、毫秒分为5个等级
     * @return XX天XX小时XX分XX秒
     */
    public static String formatBetween(Date beginDate, Date endDate, DateUnit unit) {
        /** 开始日期 */
        Date begin, end;
        if (beginDate.after(endDate)) {
            // 间隔只为正数的情况下，如果开始日期晚于结束日期，置换之
            begin = endDate;
            end = beginDate;
        } else {
            begin = beginDate;
            end = endDate;
        }
        long diff = end.getTime() - begin.getTime();
        return formatTime(diff, unit, true);
    }


    /**
     * 格式化日期间隔输出<br>
     *
     * @return 格式化后的字符串
     */
    public static String formatTime(long betweenMs, DateUnit unit, boolean all) {
        final StringBuilder sb = new StringBuilder();
        if (betweenMs > 0) {
            long day = betweenMs / DateUnit.DAY.getMillis();
            long hour = betweenMs / DateUnit.HOUR.getMillis() - day * 24;
            long minute = betweenMs / DateUnit.MINUTE.getMillis() - day * 24 * 60 - hour * 60;

            final long BetweenOfSecond = ((day * 24 + hour) * 60 + minute) * 60;
            long second = betweenMs / DateUnit.SECOND.getMillis() - BetweenOfSecond;
            long millisecond = betweenMs - (BetweenOfSecond + second) * 1000;

            int levelCount = unit.ordinal();

            if (all && 0 != day && levelCount >= DateUnit.DAY.ordinal()) {
                sb.append(day).append(DateUnit.DAY.getName());
            }
            if (all && 0 != hour && levelCount >= DateUnit.HOUR.ordinal()) {
                sb.append(hour).append(DateUnit.HOUR.getName());
            }
            if (all && 0 != minute && levelCount > DateUnit.MINUTE.ordinal()) {
                sb.append(minute).append(DateUnit.MINUTE.getName());
            }
            if (all && 0 != second && levelCount >= DateUnit.SECOND.ordinal()) {
                sb.append(second).append(DateUnit.SECOND.getName());
            }
            if (all && 0 != millisecond && levelCount >= DateUnit.MS.ordinal()) {
                sb.append(millisecond).append(DateUnit.MS);
                // levelCount++;
            }
        }

        if (StrUtil.isEmpty(sb)) {
            sb.append(0).append(unit.getName());
        }

        return sb.toString();
    }

    /**
     * 生日转为年龄，计算法定年龄
     *
     * @param birthDay 生日，标准日期字符串
     * @return 年龄
     */
    public static int ageOfNow(String birthDay) {
        return ageOfNow(parseDate(birthDay));
    }

    /**
     * 生日转为年龄，计算法定年龄
     *
     * @param birthDay 生日
     * @return 年龄
     */
    public static int ageOfNow(Date birthDay) {
        return age(birthDay, currentDate());
    }

    /**
     * 计算相对于dateToCompare的年龄，长用于计算指定生日在某年的年龄
     *
     * @param birthday      生日
     * @param dateToCompare 需要对比的日期
     * @return 年龄
     */
    public static int age(Date birthday, Date dateToCompare) {
        Assert.notNull(birthday, "Birthday can not be null !");
        if (null == dateToCompare) {
            dateToCompare = currentDate();
        }
        return age(birthday.getTime(), dateToCompare.getTime());
    }

    /**
     * 计算相对于dateToCompare的年龄，长用于计算指定生日在某年的年龄
     *
     * @param birthday      生日
     * @param dateToCompare 需要对比的日期
     * @return 年龄
     */
    protected static int age(long birthday, long dateToCompare) {
        if (birthday > dateToCompare) {
            throw new IllegalArgumentException("Birthday is after dateToCompare!");
        }

        final Calendar cal = Calendar.getInstance();
        cal.setTimeInMillis(dateToCompare);

        final int year = cal.get(Calendar.YEAR);
        final int month = cal.get(Calendar.MONTH);
        final int dayOfMonth = cal.get(Calendar.DAY_OF_MONTH);
        final boolean isLastDayOfMonth = dayOfMonth == cal.getActualMaximum(Calendar.DAY_OF_MONTH);

        cal.setTimeInMillis(birthday);
        int age = year - cal.get(Calendar.YEAR);

        final int monthBirth = cal.get(Calendar.MONTH);
        if (month == monthBirth) {

            final int dayOfMonthBirth = cal.get(Calendar.DAY_OF_MONTH);
            final boolean isLastDayOfMonthBirth = dayOfMonthBirth == cal.getActualMaximum(Calendar.DAY_OF_MONTH);
            if ((false == isLastDayOfMonth || false == isLastDayOfMonthBirth) && dayOfMonth < dayOfMonthBirth) {
                // 如果生日在当月，但是未达到生日当天的日期，年龄减一
                age--;
            }
        } else if (month < monthBirth) {
            // 如果当前月份未达到生日的月份，年龄计算减一
            age--;
        }

        return age;
    }


    /**
     * {@code null}安全的日期比较，{@code null}对象排在末尾
     *
     * @param date1 日期1
     * @param date2 日期2
     * @return 比较结果，如果date1 &lt; date2，返回数小于0，date1==date2返回0，date1 &gt; date2 大于0
     */
    public static int compare(Date date1, Date date2) {
        return CompareUtil.compare(date1, date2);
    }
}
